package com.my.spring.framework.webmvc.servlet;

import com.my.spring.framework.annotation.MyRequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class MyHandlerAdapter {
    public boolean supports(Object handler) {
        return handler instanceof MyHandlerMapping;
    }

    MyModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        MyHandlerMapping handlerMapping = (MyHandlerMapping)handler;
        Map<String, Integer> paramIndexMapping = new HashMap<>();
        Annotation[][] pa = handlerMapping.getMethod().getParameterAnnotations();
        for (int i = 0; i < pa.length; i++) {
            for (Annotation a : pa[i]) {
                if (a instanceof MyRequestParam) {
                    String paramName = ((MyRequestParam) a).value();
                    if (!"".equals(paramName.trim())) {
                        paramIndexMapping.put(paramName, i);
                    }
                }
            }
        }

        Class<?>[] paramTypes = handlerMapping.getMethod().getParameterTypes();
        for (int i = 0; i < paramTypes.length; i++) {
            Class<?> type = paramTypes[i];
            if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
                paramIndexMapping.put(type.getName(), i);
            }
        }

        Map<String, String[]> params = request.getParameterMap();

        Object[] paramValues = new Object[paramTypes.length];
        params.forEach((k, v) -> {
            if (!paramIndexMapping.containsKey(k)) {
                return;
            }
            int idx = paramIndexMapping.get(k);
            paramValues[idx] = convert(v, paramTypes[idx]);
        });

        if (paramIndexMapping.containsKey(HttpServletResponse.class.getName())) {
            int respIdx = paramIndexMapping.get(HttpServletResponse.class.getName());
            paramValues[respIdx] = response;
        }

        if (paramIndexMapping.containsKey(HttpServletRequest.class.getName())) {
            int respIdx = paramIndexMapping.get(HttpServletRequest.class.getName());
            paramValues[respIdx] = request;
        }

        // 执行方法
        Object retVal = handlerMapping.getMethod().invoke(handlerMapping.getController(), paramValues);

        if (retVal != null) {
            Class retType = handlerMapping.getMethod().getReturnType();
            if (retType == MyModelAndView.class) {
                return (MyModelAndView) retVal;
            }
            return new MyModelAndView(retVal.toString());
        }
        return null;
    }

    MyModelAndView handleV1(HttpServletRequest request, HttpServletResponse response, Object handler) throws InvocationTargetException, IllegalAccessException {
        MyHandlerMapping handlerMapping = (MyHandlerMapping)handler;
        Method method = handlerMapping.getMethod();
        Object[] args = resolveArguments(request, response, method.getParameters());
        // 执行方法
        Object retVal = method.invoke(handlerMapping.getController(), args);

        if (retVal != null) {
            Class retType = handlerMapping.getMethod().getReturnType();
            if (retType == MyModelAndView.class) {
                return (MyModelAndView) retVal;
            }
            return new MyModelAndView(retVal.toString());
        }
        return null;
    }

    private Object[] resolveArguments(HttpServletRequest request, HttpServletResponse response, Parameter[] parameters) {
        Object[] res = new Object[parameters.length];
        Map<String, String[]> paramMap = request.getParameterMap();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Class paramType = parameter.getType();

            if (HttpServletRequest.class == paramType) {
                res[i] = request;
                continue;
            }

            if (HttpServletResponse.class == paramType) {
                res[i] = response;
                continue;
            }

            MyRequestParam annotationParam = parameter.getAnnotation(MyRequestParam.class);
            // 得到的key的格式是arg0，行不通
            String paramKey = parameter.getName();
            if (annotationParam != null) {
                paramKey = annotationParam.value();
            }
            res[i] = convert(paramMap.get(paramKey), parameter.getType());
        }
        return res;
    }

    private Object convert(String[] arg, Class<?> paramType) {
        if (arg == null) {
            return null;
        }
        String value = Arrays.toString(arg).replaceAll("\\[|\\]", "")
                .replaceAll("\\s", ",");
        if (Integer.class.equals(paramType)) {
            return Integer.valueOf(value);
        }
        if (int.class.equals(paramType)) {
            return Integer.valueOf(value).intValue();
        }
        return value;
    }
}
